/*
 * fuseLayer.c
 *
 *  Created on: 17/09/2011
 *      Author: utn_so
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "structsFAT.h"
#include "fuseLayer.h"
#include "functionsFAT.h"
#include "basicFunctions.h"
#include "../../General/Stream/stream.h"

#include <time.h>

pthread_mutex_t sem_editEntrys = PTHREAD_MUTEX_INITIALIZER;

void AddListFileEntry(List_FILE_INFO * Lista, const char * name, CLUSTER firstcluster, int32_t size, int32_t attribute)
{
	Nodo_FILE_INFO * aux = (Nodo_FILE_INFO *) malloc(sizeof(Nodo_FILE_INFO));

	aux->aFile.firstCluster = firstcluster;
	aux->aFile.size = size;
	aux->aFile.attribute = attribute;
	strcpy(aux->aFile.fileName, name);
	aux->nxt = NULL;

	if (Lista->Head == NULL) {
		Lista->Head = aux;
		Lista->Tail = aux;
	}
	else {
		Lista->Tail->nxt = aux;
		Lista->Tail = aux;
	}
}

void List_FILE_INFO_Destroy(List_FILE_INFO * Listdir)
{
	Nodo_FILE_INFO * aux;
	while (Listdir->Head != NULL) {
		aux = Listdir->Head;
		Listdir->Head = Listdir->Head->nxt;
		free(aux);
	}
	Listdir->Head = NULL;
	Listdir->Tail = NULL;
}

void List_FILE_INFO_Init(List_FILE_INFO * Listdir)
{
	Listdir->Head = NULL;
	Listdir->Tail = NULL;
}

/* LFN:
	0x00 1 Sequence Number
	0x01 10 Name characters (five UTF-16 characters)
	0x0B 1 Attributes (always 0x0F)
	0x0C 1 Reserved (always 0x00)
	0x0D 1 Checksum of DOS file name
	0x0E 12 Name characters (six UTF-16 characters)
	0x1A 2 First cluster (always 0x0000)
	0x1C 4 Name characters (two UTF-16 characters)
*/

void getFileEntryLFN(STREAM * fileEntry, char * aFileName)
{
	int i   = 0;
	int pos = 0x01;
	while ((pos < 0x20) && (fileEntry->data[pos] != 0xFF)) {
		aFileName[i] = fileEntry->data[pos];
		pos += 2; /*solo se lee la parte baja de bytes*/
		(pos == 0x0B)?(pos = 0x0E):0;
		(pos == 0x1A)?(pos = 0x1C):0;
		i++;
	}
	aFileName[i] = '\0';
}

int LFN(STREAM * fileEntry)
{
	if (fileEntry->data[0x0B] == 0x0F)
		return 1; //TRUE
	else
		return 0; //FALSE
}

void getFileEntry(STREAM * fileEntry, char * aFileName) //Verificar si es necesario entradas sin lfn
{
	int i   = 0;
	int pos = 0x00;
	for (i = 0; i < 8 && fileEntry->data[pos] != ' '; i++) {
		aFileName[i] = fileEntry->data[pos];
		pos++;
	}
	//agrego la extension
	pos = 0x08;
	if(fileEntry->data[pos] != ' ') {//tiene extension
		aFileName[i] = '.';
		i++;
	}
	while(i < 0x0B && fileEntry->data[pos] != ' ') {
		aFileName[i] = fileEntry->data[pos];
		i++;
		pos++;
	}

	aFileName[i] = '\0';
}

int validateFile(STREAM * aFileEntry)
{
	//Validaciones sobre archivo
	return aFileEntry->data[0x00] != 0x00 &&
		   aFileEntry->data[0x0B] != 0x80 && /*unused*/
		   aFileEntry->data[0x00] != 0xE5 && /*deleted file*/
		   //posibles valores de los atributos
		   (aFileEntry->data[0x0B] == 0x0F ||
			aFileEntry->data[0x0B] == 0x10 ||
			aFileEntry->data[0x0B] == 0x20  );
}

void setAttrFile(STREAM * fileEntry, CLUSTER * firstcluster, int32_t * filesize, int32_t * attribute)
{
	(*firstcluster) = fileEntry->data[0x15] * 0x1000000 + fileEntry->data[0x14] * 0x10000 +
				   	  fileEntry->data[0x1B] * 0x100		+ fileEntry->data[0x1A];
	(*filesize) = fileEntry->data[0x1F] * 0x1000000 + fileEntry->data[0x1E] * 0x10000 +
			   	  fileEntry->data[0x1D] * 0x100     + fileEntry->data[0x1C];
	(*attribute) = fileEntry->data[0x0B];
}

void setListFiles(List_FILE_INFO * Listdir, STREAM * buffer, int32_t size)
{
	int32_t offset = 0;
	int32_t count  = 0;
	STREAM fileEntry;
	unsigned char * ptr;
	char aFileName[MAX_FILENAME_SO];
	CLUSTER firstcluster = 0;
	int32_t filesize = 0;
	int32_t attribute = 0;

	fileEntry.data = (unsigned char *) malloc(32 * sizeof(unsigned char));
	fileEntry.length = 32;
	ptr = buffer->data;
	while (count < size) {
		fileEntry.data = ptr;
		if (validateFile(&fileEntry)) {
			if (LFN(&fileEntry)) {
				getFileEntryLFN(&fileEntry, aFileName);
				fileEntry.data = ptr + 32; //voy a la sig entrada que es la que tiene los attr
				setAttrFile(&fileEntry, &firstcluster, &filesize, &attribute);
				offset = 64;
				ptr += offset;
				count += offset;
			}
			else {
				getFileEntry(&fileEntry, aFileName);
				setAttrFile(&fileEntry, &firstcluster, &filesize, &attribute);
				offset = 32;
				ptr += offset;
				count += offset;
			}
			if (validateFile(&fileEntry))  //entonces vuelvo a validar que la entrada sea valida
				AddListFileEntry(Listdir, aFileName, firstcluster, filesize, attribute);
		}
		else {
			if (fileEntry.data[0x00] == 0x00) break;
			offset = 32;
			ptr += offset;
			count += offset;
		}
	}
}

FILE_INFO ListFiles_GetFileInfoFromFileName(List_FILE_INFO *Listdir, const char * aFileName)
{
	Nodo_FILE_INFO * aux;
	aux = Listdir->Head;
	while (aux != NULL) {
		if (strcasecmp(aFileName, aux->aFile.fileName) == 0)
			return aux->aFile;
		aux = aux->nxt;
	}
	return aux->aFile; //Not Found
}

int SearchListFiles(List_FILE_INFO * Listdir, const char * aFileName)
{
	Nodo_FILE_INFO * aux;
	aux = Listdir->Head;
	while (aux != NULL) {
		if (strcasecmp(aFileName, aux->aFile.fileName) == 0)
			return 0;
		aux = aux->nxt;
	}
	return -1; //Not Found
}


int getEntrysFromPath(const char * path, List_FILE_INFO *Listdir)
{
	int Result = 0;
	STREAM buffer;
	CLUSTER ptrCluster = getRootCluster();
	char * saveptr;
	char * ptrPath;
	char * aFileName;
	FILE_INFO fileInfo;

	//Read of root cluster
	readBufferFromCluster(ptrCluster, getSizeOfCluster(), &buffer, path, 0);
	setListFiles(Listdir, &buffer, getSizeOfCluster());
	free(buffer.data);
	Listdir->firstCluster = ptrCluster;
	ptrCluster = NextCluster(ptrCluster);
	while (!EOC(ptrCluster)) {
		readBufferFromCluster(ptrCluster, getSizeOfCluster(),&buffer, path, 0);
		setListFiles(Listdir, &buffer, getSizeOfCluster());
		free(buffer.data);
		ptrCluster = NextCluster(ptrCluster);
	}

	if (strcmp(path, "/") == 0) {//Is Root Directory
		return 0;
	}

	ptrPath = malloc((strlen(path) + 1)*sizeof(char));
	strcpy(ptrPath, path);
	aFileName = strtok_r(ptrPath, "/", &saveptr);
	do {
		if (SearchListFiles(Listdir, aFileName) != -1) { //(Found)
			fileInfo = ListFiles_GetFileInfoFromFileName(Listdir, aFileName);
			readBufferFromCluster(fileInfo.firstCluster, getSizeOfCluster(), &buffer, path, 0);
			List_FILE_INFO_Destroy(Listdir);
			ptrCluster = fileInfo.firstCluster;
			while (!EOC(ptrCluster)) {
				setListFiles(Listdir, &buffer, getSizeOfCluster());
				ptrCluster = NextCluster(ptrCluster);
			}
			Listdir->firstCluster = fileInfo.firstCluster;
			free(buffer.data);
		} else {
			Result = -1;
			break;
		}
		aFileName = strtok_r(NULL, "/", &saveptr);
	} while (aFileName != NULL);

	free(ptrPath);

	return Result;
}

int readBootSector()
{
	return setInfoBootSector();
}

int loadFileAllocationTable()
{
	return setFileAllocationTable();
}

void FreeFileAllocationTable()
{
	freeFileAllocationTable();
}

void writeCluster(STREAM aStream, int32_t offset, int32_t size, CLUSTER cluster, const char * path)
{
	STREAM buffer;
	buffer.data = aStream.data + offset;
	buffer.length = size;

	writeBufferToCluster(cluster, &buffer, path);
}

int writeClusterList(STREAM aStream, int32_t cantClusters, CLUSTER * newFirstCluster, const char * path)
{
	Fifo_CLUSTER clusterList;
	Nodo_CLUSTER * nodoCluster;
	int Result = 0;
	int32_t i = 0, offset = 0, size = 0;

	FIFO_CLUSTER_Create(&clusterList);

	//Pido la cantidad de clusters necesaria
	getAndSetListOfFreeClusters(&clusterList, cantClusters);
	if(clusterList.Head == NULL) Result = -1;
	if (Result != -1) {
		nodoCluster = clusterList.Head;
		i = 0;
		size = aStream.length;
		Result = size;

		if ((* newFirstCluster) == 0)
			(* newFirstCluster) = nodoCluster->cluster;
		else
			setCluster((*newFirstCluster), nodoCluster->cluster);

		while (nodoCluster != NULL && size > 0) {
			if (nodoCluster->nxt != NULL)
				setCluster(nodoCluster->cluster, nodoCluster->nxt->cluster);
			else
				setCluster(nodoCluster->cluster, EndCluster);
			if (size >= getSizeOfCluster())
				writeCluster(aStream, offset, getSizeOfCluster(), nodoCluster->cluster, path);
			else
				writeCluster(aStream, offset, size, nodoCluster->cluster, path);
			size -= getSizeOfCluster();
			offset += getSizeOfCluster();
		}
		UpdateFileAllocationTable();
	}

	FIFO_CLUSTER_Destroy(&clusterList);
	return Result;
}

int writeDataToFile(const char * path, const char * buf, int32_t size, CLUSTER * newFirstCluster, uint32_t offset, FILE_INFO * aFileInfo)
{
 	STREAM aStream;

	int Result = 0;
	CLUSTER aCluster, lastCluster;
	Fifo_CLUSTER clusterList;
	Nodo_CLUSTER * nodoCluster;

	int32_t cantClustersNeeded = 0;

	if ((*newFirstCluster) != 0) {
		FIFO_CLUSTER_Create(&clusterList);
		if (aFileInfo->size < offset + size) {
			cantClustersNeeded = (offset + size - aFileInfo->size) / getSizeOfCluster();
			if (!(size <= (aFileInfo->size % getSizeOfCluster())) && //no me alcanza lo que me queda del cluster
				((offset + size - aFileInfo->size) % getSizeOfCluster() != 0))
				cantClustersNeeded++;
			getAndSetListOfFreeClusters(&clusterList, cantClustersNeeded);
			aCluster = aFileInfo->firstCluster;
			lastCluster = aCluster;
			while (!EOC(aCluster)) {
				lastCluster = aCluster;
				aCluster = NextCluster(aCluster);
			}
			nodoCluster = clusterList.Head;
			while(nodoCluster != NULL) {
				setCluster(lastCluster, nodoCluster->cluster);
				lastCluster = nodoCluster->cluster;
				if (nodoCluster->nxt == NULL)
					setCluster(lastCluster, EndCluster);
				nodoCluster = nodoCluster->nxt;
			}

		}
		aCluster = aFileInfo->firstCluster;
		while(size > 0) {
			if (offset >= getSizeOfCluster())
				aCluster = NextCluster(aCluster);
			else {
				readBufferFromCluster(aCluster, getSizeOfCluster(), &aStream, path, 0);
				if (size < getSizeOfCluster() - offset) {
					memcpy(aStream.data + offset, buf, size);
					buf += size;
					Result += size;
					size = 0;
				}
				else {
					memcpy(aStream.data + offset, buf, getSizeOfCluster() - offset);
					buf += getSizeOfCluster() - offset;
					Result += size;
					size -= getSizeOfCluster() - offset;
				}
				writeBufferToCluster(aCluster, &aStream, path);
				STREAM_Free(&aStream);
				aCluster = NextCluster(aCluster);
			}
			offset -= getSizeOfCluster();
		}
		UpdateFileAllocationTable();
		FIFO_CLUSTER_Destroy(&clusterList);
	}
	else {
		STREAM_Alloc(&aStream, size);
		memcpy(aStream.data, buf, size);
		aStream.length = size;
		cantClustersNeeded = size / getSizeOfCluster();
		if (size % getSizeOfCluster() != 0)
			cantClustersNeeded += 1; /*necesito 1 cluster mas para lo que sobro*/

		Result = writeClusterList(aStream, cantClustersNeeded, newFirstCluster, path);
		STREAM_Free(&aStream);
	}

	return Result;
}

void getPointerToFile(STREAM * buffer, const char * aFileName, unsigned char ** PtrOut)
{
	/*En esta funcion se asume que buffer pertenece a un cluster directorio*/
	int32_t offset = 0;
	int32_t count = 0;
	STREAM fileEntry;
	unsigned char * ptr;
	char aFile[MAX_FILENAME_SO];

	fileEntry.data = (unsigned char *) malloc(32 * sizeof(unsigned char));
	fileEntry.length = 32;

	ptr = buffer->data;
	fileEntry.data = ptr;

	while (count < buffer->length && fileEntry.data[0x00] != 0x00) {
		if ((strcmp(aFileName, "$") == 0) && fileEntry.data[0x00] == 0xE5) { //$ esta buscando un espacio libre para la entrada
			(*PtrOut) = ptr;
			break;
		}
		if (LFN(&fileEntry)) {
			getFileEntryLFN(&fileEntry, aFile);
			if (strcasecmp(aFileName, aFile) == 0) {
				ptr += 32; //Posiciono en la entrada de archivo de los attr
				(*PtrOut) = ptr; //Asigno al puntero el puntero que comienza al archivo
				break;
			}
			offset = 64;
			ptr += offset;
			count += offset;
		}
		else {
			getFileEntry(&fileEntry, aFile);
			if (strcasecmp(aFileName, aFile) == 0) {
				(*PtrOut) = ptr; //Asigno al puntero la parte alta
				break;
			}
			offset = 32;
			ptr += offset;
			count += offset;
		}
		fileEntry.data = ptr;
	}
	if (strcmp(aFileName, "$") == 0) //$ esta buscando un espacio libre para la entrada
		(*PtrOut) = ptr + 32; //Posiciono en la entrada que seria de info, estando arriba la de lfn
}

void writeClusterEmptyDirectory(CLUSTER aCluster)
{
	STREAM buffer;
	STREAM_Alloc(&buffer, getSizeOfCluster());
	int32_t prox = 0x20;
	int32_t i;

	buffer.data[0x00] = 0x2E;
	for(i = 0x01; i < 0x0B; i++)
		buffer.data[i] = 0x20;

	buffer.data[0x0B] = 0x10;
	buffer.data[0x14] = aCluster / 0x10000;
	buffer.data[0x1A] = aCluster % 0x10000;

	buffer.data[prox + 0x00] = 0x2E;
	buffer.data[prox + 0x01] = 0x2E;
	for(i = 0x02; i < 0x0B; i++)
		buffer.data[prox + i] = 0x20;
	buffer.data[prox + 0x0B] = 0x10;
	buffer.data[prox + 0x14] = 0;
	buffer.data[prox + 0x1A] = 0;

	buffer.length = prox + 0x1A;
	writeBufferToCluster(aCluster, &buffer, "");

	STREAM_Free(&buffer);
}

void setLFN(unsigned char * LFNEntry, const char * fileName)
{
	int32_t i = 0;
	int32_t j = 0x01;

	LFNEntry[0x00] = 0x41; //Como solo se maneja 1 entrada de lfn, tiene numero de secuencia = A
	LFNEntry[0x0B] = 0x0F;
	LFNEntry[0x0C] = 0x00;
	LFNEntry[0x1A] = 0x00;
	LFNEntry[0x1B] = 0x00;
	//Hay que buscar la biblioteca utf16
	while (fileName[i] != '\0') {
		LFNEntry[j] = fileName[i];
		LFNEntry[j + 1] = 0x00;
		i++;
		j += 0x02;
		(j == 0x0B) ? j = 0x0E : 0;
		(j == 0x1A) ? j = 0x1C : 0;
	}

	//Asigno 0xFF a los bytes que me sobran de las entradas
	while(j < 0x20) {
		LFNEntry[j] = 0xFF;
		LFNEntry[j + 1] = 0xFF;
		j += 0x02;
		(j == 0x0B) ? j = 0x0E : 0;
		(j == 0x1A) ? j = 0x1C : 0;
	}

//	0x01 10 Name characters (five UTF-16 characters)
//	0x0E 12 Name characters (six UTF-16 characters)
//	0x1C 4 Name characters (two UTF-16 characters) (como en el TP solo se manejan nombres de 13 incluyendo la extension, no es necesario)
}

void setFileEntry(unsigned char * fileEntry, FILE_INFO aFile)
{
	//Hay que manejar las fechas??
	//Cuando el nombre es mas largo que 8 hay que asignar el nombre con ~1 ??
	int32_t i, j, posExt;

	posExt = 0;
	while((posExt < MAX_FILENAME_SO - 1) && (aFile.fileName[posExt] != '.')) posExt++; //Busco la extension

	for (i = 0x00; i < 0x08 && i < posExt; i++)
		fileEntry[i] = aFile.fileName[i];

	i = posExt + 1;
	j = 0x08;
	while (i < MAX_FILENAME_SO - 1) {
		fileEntry[j] = aFile.fileName[i];
		i++;
		j++;
	}
	while (j < 0x0B) {
		fileEntry[j] = 0x20; //Padeo con espacios
		j++;
	}
	fileEntry[0x0B] = aFile.attribute;

	//Asignacion del primer cluster
	fileEntry[0x14] = (aFile.firstCluster / 0x10000) % 0x100; //Cambio la parte alta del primer cluster del archivo
	fileEntry[0x15] = (aFile.firstCluster / 0x10000) / 0x100; //Cambio la parte alta del primer cluster del archivo
	fileEntry[0x1A] = (aFile.firstCluster % 0x10000) % 0x100; //Cambio la parte baja del primer cluster del archivo
	fileEntry[0x1B] = (aFile.firstCluster % 0x10000) / 0x100; //Cambio la parte baja del primer cluster del archivo

	//Asignacion de tamaño
	fileEntry[0x1C] = aFile.size % 0x100;
	fileEntry[0x1D] = (aFile.size % 0x10000) /0x100;
	fileEntry[0x1E] = (aFile.size % 0x1000000) /0x10000;
	fileEntry[0x1F] = aFile.size / 0x1000000;

	fileEntry[0x10] = aFile.createDate % 0x100;
	fileEntry[0x11] = aFile.createDate / 0x100;

}

int AddEntryToDirectory(List_FILE_INFO * aListDirectory, FILE_INFO aFile, int isDirectory, int create)
{
	STREAM buffer;
	unsigned char * Ptr = NULL;
	unsigned char * fileEntry = NULL;
	unsigned char * LFNEntry  = NULL;
	CLUSTER ptrCluster;

	//Bloqueo del directorio donde se va a agregar la entrada, esto es necesario porque en un entorno de concurrencia se pueden pisar las entradas de archivos que se esten agregando en el mismo directorio
	pthread_mutex_lock(&sem_editEntrys);

	ptrCluster = aListDirectory->firstCluster;
	do {
		readBufferFromCluster(ptrCluster, getSizeOfCluster(), &buffer, "", 0);
		getPointerToFile(&buffer, "$", &Ptr);
		if (Ptr != NULL) break;
		ptrCluster = NextCluster(ptrCluster);
		STREAM_Free(&buffer);
	} while (!EOC(ptrCluster));

	if (Ptr == NULL) {
		pthread_mutex_unlock(&sem_editEntrys);
		return -1;
	}

	fileEntry = Ptr;
	LFNEntry  = Ptr - 32;

	setLFN(LFNEntry, aFile.fileName);
	setFileEntry(fileEntry, aFile);

	writeBufferToCluster(ptrCluster, &buffer, ""); //Escritura del cluster donde estan las entradas del directorio
	if (isDirectory && create) {
		setCluster(aFile.firstCluster, 0x0FFFFFFF);
		writeClusterEmptyDirectory(aFile.firstCluster); //Escritura del cluster donde estan las entradas . y .. del nuevo directorio
	}
	STREAM_Free(&buffer);
	if (create)
		UpdateFileAllocationTable();

	pthread_mutex_unlock(&sem_editEntrys);
	return 0;
}

int RemoveEntryFromDirectory(List_FILE_INFO * aListDirectory, FILE_INFO aFile)
{
	STREAM buffer;
	unsigned char * Ptr = NULL;
	CLUSTER ptrCluster;

	pthread_mutex_lock(&sem_editEntrys);

	ptrCluster = aListDirectory->firstCluster;
	do {
		readBufferFromCluster(ptrCluster, getSizeOfCluster(), &buffer, "", 0);
		getPointerToFile(&buffer, aFile.fileName, &Ptr);
		if (Ptr != NULL) break;
		ptrCluster = NextCluster(ptrCluster);
		STREAM_Free(&buffer);
	} while (!EOC(ptrCluster));

	if (Ptr == NULL) {
		pthread_mutex_unlock(&sem_editEntrys);
		return -1;
	}

	Ptr[0] = 0xE5;  //Seteando el primer byte de la entrada de archivo indico que ese archivo fue eliminado
	Ptr[-32] = 0xE5; //Seteo tambien el primer byte de la LFN

	writeBufferToCluster(ptrCluster, &buffer, "");

	pthread_mutex_unlock(&sem_editEntrys);
	STREAM_Free(&buffer);
	return 0;
}

void copyFileInfo(FILE_INFO * aFileTo, FILE_INFO aFileFrom)
{
	strcpy(aFileTo->fileName, aFileFrom.fileName);
	aFileTo->firstCluster = aFileFrom.firstCluster;
	aFileTo->size = aFileFrom.size;
	aFileTo->attribute = aFileFrom.attribute;
}

int setFileInfoByFileName(List_FILE_INFO * Listdir, const char * aFileName, FILE_INFO * aFile)
{
	Nodo_FILE_INFO * aux;
	aux = Listdir->Head;
	while (aux != NULL) {
		if (strcasecmp(aFileName, aux->aFile.fileName) == 0) {
			copyFileInfo(aFile, aux->aFile);
			return 0;
		}
		aux = aux->nxt;
	}
	return -1; //Not Found
}

int freeChainCluster(CLUSTER firstCluster)
{
	int R = FreeChainCluster(firstCluster);
	UpdateFileAllocationTable();
	return R;
}

int freeChainCluster_noUp(CLUSTER firstCluster)
{
	return FreeChainCluster(firstCluster);
}

int truncateChainCluster(FILE_INFO * aFile, int32_t size)
{
	//Solo funcion de truncate cuando size < file.size
	int32_t clustersNeeded = 0,
			lastCluster = 0;
	CLUSTER aCluster = aFile->firstCluster;
	Fifo_CLUSTER listNewClusters;
	Nodo_CLUSTER *Aux;
	STREAM aStream;

	STREAM_Init(&aStream);
	FIFO_CLUSTER_Create(&listNewClusters);
	if(size < aFile->size) {
		lastCluster = 0;
		while(size > 0) {
			lastCluster = aCluster;
			aCluster = NextCluster(aCluster);
			size -= getSizeOfCluster();
		}
		/*Los clusters que me quedan sobran*/
		if (!EOC(aCluster))
			freeChainCluster(aCluster);
		if (lastCluster != 0)
			setCluster(lastCluster, EndCluster);
	}
	else if (size > aFile->size) {
		size = size - aFile->size;
		clustersNeeded = (size / getSizeOfCluster()); /*Cant de clusters totales - Cant de clusters que ya tengo*/

		if (size % getSizeOfCluster() != 0)
			clustersNeeded++;

		if (aFile->firstCluster == 0) {
			aFile->firstCluster = getFreeCluster();
			clustersNeeded--; //asigno el primer cluster porque no tiene, entonces necesito un cluster menos
		}

		aCluster = aFile->firstCluster;

		while(!EOC(aCluster)) {
			lastCluster = aCluster;
			aCluster = NextCluster(lastCluster);
		}
		aCluster = lastCluster;

		if (clustersNeeded > 0) //si no es mayor que 0 => list = NULL
			getAndSetListOfFreeClusters(&listNewClusters, clustersNeeded);
		else { /*escribo el ultimo cluster pero con la cantidad de bytes correspondiente y el resto 00*/
			readBufferFromCluster(lastCluster,aFile->size % getSizeOfCluster(), &aStream, "", 0);
			writeBufferToCluster(lastCluster, &aStream, "");
		}

		writeBufferToCluster(aCluster, &aStream, "");
		Aux = listNewClusters.Head;
		while (Aux != NULL) {
			setCluster(aCluster, Aux->cluster);
			aCluster = Aux->cluster;
			writeBufferToCluster(aCluster, &aStream, ""); /*escribo en el datos con 00 en el cluster*/
			Aux = Aux->nxt;
		}
		setCluster(aCluster, 0x0FFFFFFF);//EndCluster
		UpdateFileAllocationTable();
	}

	STREAM_Free(&aStream);
	FIFO_CLUSTER_Destroy(&listNewClusters);
	return 0;
}

int UpdateClusterFile(CLUSTER ClusterToUpdate,FILE_INFO fileInfo)
{
	STREAM buffer;
	unsigned char * Ptr = NULL;

	pthread_mutex_lock(&sem_editEntrys);
	readBufferFromCluster(ClusterToUpdate, getSizeOfCluster(), &buffer, "", 0);
	//Cambiar buffer
	getPointerToFile(&buffer, fileInfo.fileName, &Ptr);
	Ptr[0x14] = (fileInfo.firstCluster / 0x10000) % 0x100; //Cambio la parte alta del primer cluster del archivo
	Ptr[0x15] = (fileInfo.firstCluster / 0x10000) / 0x100; //Cambio la parte alta del primer cluster del archivo
	Ptr[0x1A] = (fileInfo.firstCluster % 0x10000) % 0x100; //Cambio la parte baja del primer cluster del archivo
	Ptr[0x1B] = (fileInfo.firstCluster % 0x10000) / 0x100; //Cambio la parte baja del primer cluster del archivo
	//Asignacion del tamaño
	Ptr[0x1C] = fileInfo.size % 0x100;
	Ptr[0x1D] = (fileInfo.size % 0x10000) /0x100;
	Ptr[0x1E] = (fileInfo.size % 0x1000000) /0x10000;
	Ptr[0x1F] = fileInfo.size / 0x1000000;

	writeBufferToCluster(ClusterToUpdate, &buffer, "");

	STREAM_Free(&buffer);
	pthread_mutex_unlock(&sem_editEntrys);
	return 0;
}

CLUSTER getFreeCluster()
{
	CLUSTER Result;
	Fifo_CLUSTER clusterForNewDir;
	FIFO_CLUSTER_Create(&clusterForNewDir);
	getAndSetListOfFreeClusters(&clusterForNewDir, 1);
	Result = clusterForNewDir.Head->cluster;
	FIFO_CLUSTER_Destroy(&clusterForNewDir);
	return Result;
}

int readDataFromChainCluster(CLUSTER firstCluster, char * bufferOut, int32_t size, const char * path, uint32_t offset)
{
	CLUSTER aCluster = firstCluster;
	STREAM TempStream;
	int32_t off_buff = 0;

	while (offset > 0) {
		if (offset < getSizeOfCluster())
			break;
		aCluster = NextCluster(aCluster);
		offset -= getSizeOfCluster();
	}
	//Despues del bucle anterior offset tiene el desplazamiento desde el cluster correspondiente (0<=offset<4096)

	while (!EOC(aCluster) && size > 0) {
		if (size > getSizeOfCluster()) {
			readBufferFromCluster(aCluster, getSizeOfCluster(), &TempStream, path, 0);
		}
		else {
			readBufferFromCluster(aCluster, size, &TempStream, path, offset);
			TempStream.length = size;
		}
		size -= getSizeOfCluster();
		memcpy(bufferOut + off_buff, TempStream.data + offset, TempStream.length);
		offset = 0; //solo usa el desplazamiento en el primer cluster
		off_buff += TempStream.length;
		STREAM_Free(&TempStream);
		aCluster = NextCluster(aCluster);
	}
	return 0;
}

void assignCreateDate(FILE_INFO * aFile)
{
    int32_t year; /*0-127*/ /*15-9*/
    int32_t month; /*0-15*/  /*8-5*/
    int32_t day; /*0-31*/  /*4-0*/
    int32_t fecha;
    time_t t;
    struct tm *date;

    t = time(NULL);
    date = localtime(&t);

    year = date->tm_year + 1900 - 1980;
    month = date->tm_mon + 1;
    day = date->tm_mday;

    if (date == NULL) {
    	aFile->createDate = 0;
    	return;
    }
    fecha = year;
    fecha = fecha << 9;
    fecha = fecha | (month << 5);
    fecha = fecha | day;

	aFile->createDate = fecha;
}

void logChainClusters(CLUSTER firstCluster, char ** strlog)
{
	char strcluster[11]; //11 = digitos max de uint32 (10) + \0 (1)
	uint32_t cantClusters = 0;
	Fifo_CLUSTER clusterList;
	Nodo_CLUSTER *ptrNCluster;

	FIFO_CLUSTER_Create(&clusterList);
	getChainClusters(firstCluster, &clusterList, &cantClusters);
	(*strlog) = (char *)calloc(cantClusters * 12, sizeof(char));//12 = digitos max de uint32 (10) + strlen(',') + \0 (1)
	strcpy((*strlog), "");
	ptrNCluster = clusterList.Head;
	while (ptrNCluster != NULL) {
		intToStr(ptrNCluster->cluster, strcluster);
		strcat((*strlog), strcluster);
		ptrNCluster = ptrNCluster->nxt;
		if (ptrNCluster != NULL)
			strcat((*strlog), ",");
	}
	FIFO_CLUSTER_Destroy(&clusterList);
}
